package com.idol.framework.servlet;

import com.idol.framework.builder.ConfigBuilder;
import com.idol.framework.factory.BeanFactory;
import com.idol.framework.handler.Handler;
import com.idol.framework.handler.HandlerMapping;
import com.idol.framework.util.ScanUtil;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

/**
 * @author Supreme_Sir
 * @version 1.0
 * @className MyDispatcherServlet
 * @description 自定义请求分发处理器
 * @date 2020/10/31 13:52
 **/
public class MyDispatcherServlet extends HttpServlet {
    private BeanFactory beanFactory = BeanFactory.getInstance();
    private HandlerMapping handlerMapping = HandlerMapping.getInstance();

    @Override
    public void init(ServletConfig config) throws ServletException {
        String contextConfigLocation = config.getInitParameter("contextConfigLocation");
        try {
            // 加载配置文件
            Properties configProperties = ConfigBuilder.doLoadConfig(contextConfigLocation);
            // 扫描注解
            List<String> classPathList = ScanUtil.doScan((String) configProperties.getProperty("scanPackage"), new ArrayList<String>());
            // 初始化相关 Bean
            beanFactory.doCreateBeans(classPathList);
            // 实现依赖注入
            beanFactory.doAutoWired();
            // 构造处理器映射器——HandlerMapping，以建立 URL 与处理方法间的映射关系
            handlerMapping.initHandlerMapping();
        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("my mvc 初始化完成");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");

        Handler matchedHandler = handlerMapping.getMatchedHandler(req);
        if(matchedHandler == null) {
            resp.getWriter().write("对应的请求处理器没有被找到~！");
            return;
        }

        String name = req.getParameter("name");
        if (!matchedHandler.getUserPermission().contains(name)) {
            resp.getWriter().write("对不起，您没有访问该网络地址的权限~！");
            return;
        }

        int paramCount = matchedHandler.getMethod().getParameters().length;
        Object[] args = new Object[paramCount];

        /*
        说明：
        web 容器（Tomcat）考虑到如果一个 URL 请求如下时：http://localhost:8080/demo/query?name=lisi&name=zhangsan
        即同一个参数名多次出现的情况可能存在，所以就将同一个参数名下的值封装进一个数组中（["lisi", "zhangsan"]）
        (当然如果参数名只出现一次也会封装进字符串数组)，因此 parameterMap 的 value 泛型为 String[]。
         */
        Map<String, String[]> parameterMap = req.getParameterMap();
        for (Map.Entry entry : parameterMap.entrySet()) {
            Map<String, Integer> indexMapping = matchedHandler.getParamIndexMapping();
            if (!indexMapping.containsKey(entry.getKey())) {
                continue;
            }
            Integer index = indexMapping.get(entry.getKey());
            args[index] = StringUtils.join((String[]) entry.getValue(), ",");
        }

        // 判断是否需要 HttpServletRequest 对象作为方法参数
        if (matchedHandler.isNeedRequest()) {
            Integer requestIndex = matchedHandler.getParamIndexMapping().get("HttpServletRequest");
            args[requestIndex] = req;
        }

        // 判断是否需要 HttpServletResponse 对象作为方法参数
        if (matchedHandler.isNeedResponse()) {
            Integer responseIndex = matchedHandler.getParamIndexMapping().get("HttpServletResponse");
            args[responseIndex] = resp;
        }

        try {
            matchedHandler.getMethod().invoke(matchedHandler.getObject(), args);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        doPost(req, resp);
    }
}
